let obj = {
    name: '啦啦啦',
    say: function (...args) {
        console.log(this.name);
        console.log(args);
    }
}
let obj2 = {
    name: '哈哈哈'
}

// 手写call
obj.say.call(obj2, 'call', 2, 3);
Function.prototype.myCall = function (ctx, ...args) {
    // ctx上下文赋值
    if (ctx === null || ctx === undefined) {
        ctx = globalThis || window;
    }
    // 创建唯一属性值，防止污染源对象
    const fn = Symbol(0);
    // 把当前函数的this赋值给上下文中的函数
    ctx[fn] = this;
    // 由上下文中的函数调用，根据this指向规则：this指向运行时的函数本身，故达到将this指向ctx上下文中
    let result = ctx[fn](...args);
    // 最后删除此次创建的临时函数
    delete ctx[fn];
    return result
}
obj.say.myCall(obj2, 'myCall', 2, 3);
console.log('---------------------------');


// 手写apply
obj.say.apply(obj2, ['apply', 2, 3]);
Function.prototype.myApply = function (ctx, args = []) {
    if (ctx === null || ctx === undefined) {
        ctx = globalThis || window;
    }
    if (!args || !Array.isArray(args)) {
        throw (new Error('参数不是数组'))
    }
    const fn = Symbol(0);
    ctx[fn] = this;
    ctx[fn](...args);
    delete ctx[fn];
}
obj.say.myApply(obj2, ['myApply', 2, 3]);
console.log('---------------------------');

// 手写bind
obj.say.bind(obj2, 'bind')('2');
Function.prototype.myBind = function (ctx, ...args) {
    if (ctx === null || ctx === undefined) {
        ctx = globalThis || window;
    }
    const fn = Symbol(0);
    ctx[fn] = this;
    return (...newArgs) => {
        ctx[fn](...[...args, ...newArgs]);
        delete ctx[fn];
    }
}
obj.say.myBind(obj2, 'myBind')('2');
